﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.UI.WebControls;
using System.Reflection;
using System.Web.UI;
using Q.Framework.Data;
using Q.Framework.Extension;

namespace Q.Framework.WebControls
{
    public class BindablePanel : Panel
    {
        private List<IBindableControl> _bindableControls;
        private List<IBindableControl> bindableControls
        {
            get
            {
                if (_bindableControls == null)
                    _bindableControls = this.Controls.Cast<Control>().Where(c => typeof(IBindableControl).IsAssignableFrom(c.GetType())).Select(s => (IBindableControl)s).ToList();
                return _bindableControls;
            }
        }

        public void Bind(object value)
        {
            if (value != null)
            {
                foreach (var item in bindableControls)
                {
                    HierarchicalDataProperty prop = GetProperty(value.GetType(), item.DataPropertyName, item);
                    SetControlValue(value, prop, item);

                }
            }
        }

        public T FillProperties<T>(T obj)
        {
            foreach (var item in bindableControls.Where(b => b.Enabled))
            {
                HierarchicalDataProperty prop = GetProperty(typeof(T), item.DataPropertyName, item);
                String propertyTypeName = prop.Property.PropertyType.Name;
                PropertyInfo controlProperty = item.GetType().GetProperty(item.ControlBindableProperty);
                Type controlBindType = controlProperty.PropertyType;
                if (controlBindType != typeof(String))
                {
                    prop.Property.SetValue(obj, controlProperty.GetValue(item, null), null);
                }
                else
                {
                    String value = controlProperty.GetValue(item, null) as String;
                    if (!prop.Property.PropertyType.IsEnum)
                    {
                        if (prop.Property.PropertyType.IsValueType)
                        {
                            prop.Property.SetValue(obj, value.Parse(prop.Property.PropertyType), null);
                        }
                        else
                        {
                            if (value.GetType().GetProperties().Any(p => p.GetCustomAttributes(typeof(PrimaryKey), false).Count() > 0))
                            {
                                Type daoType = typeof(DAO<>).MakeGenericType(prop.Property.PropertyType);
                                object id = value.GetType().GetProperties().First(p => p.GetCustomAttributes(typeof(PrimaryKey), false).Count() > 0).GetValue(value);
                                prop.Property.SetValue(obj, daoType.GetMethod("GetById").Invoke(Activator.CreateInstance(daoType), new object[] { id }), null);
                            }
                        }
                    }
                    else
                    {
                        prop.Property.SetValue(obj, EnumExtension.GetEnum(prop.Property.PropertyType, value), null);
                    }
                }
            }

            return obj;
        }

        private HierarchicalDataProperty GetProperty(Type type, String name, IBindableControl control)
        {
            String[] names = name.Split('.');
            HierarchicalDataProperty retorno = new HierarchicalDataProperty();

            PropertyInfo prop = type.GetProperty(names[0]);
            if (prop == null)
                throw new Exception(String.Format("Tipo '{0}' não possui propriedade com o nome '{1}'. Favor verificar a propriedade 'DataPropertyName' do controle '{2}'.", type.Name, name, control.ID));
            else
            {
                retorno.PropertyName = names[0];
                retorno.Property = prop;
                retorno.Type = type;

                String newName = String.Join(".", names.Skip(1)).Trim();

                if (!String.IsNullOrWhiteSpace(newName))
                    retorno.ChildProperty = GetProperty(prop.PropertyType, newName, control);
            }

            return retorno;
        }

        private void SetControlValue(object value, HierarchicalDataProperty prop, IBindableControl control)
        {
            object propertyValue = prop.Property.GetValue(value);
            if (propertyValue == null)
                return;

            if (prop.ChildProperty != null)
                SetControlValue(propertyValue, prop.ChildProperty, control);
            else
            {
                PropertyInfo property = control.GetType().GetProperty(control.ControlBindableProperty);
                if (property.PropertyType != typeof(String))
                {
                    property.SetValue(control, propertyValue, null);
                }
                else
                {
                    if (!prop.Property.PropertyType.IsEnum)
                    {
                        if (prop.Property.PropertyType.IsValueType)
                        {
                            String typeName = prop.Property.PropertyType.Name;
                            switch (typeName)
                            {
                                case "String": property.SetValue(control, propertyValue, null);
                                    break;
                                case "Decimal": property.SetValue(control, ((Decimal)propertyValue).Format(), null);
                                    break;
                                case "Int32": property.SetValue(control, ((Int32)propertyValue).ToString(), null);
                                    break;
                                default:
                                    property.SetValue(control, propertyValue.ToString(), null);
                                    break;
                            }
                        }
                        else
                        {
                            if (value.GetType().GetProperties().Any(p => p.GetCustomAttributes(typeof(PrimaryKey), false).Count() > 0))
                                property.SetValue(control, propertyValue.GetType().GetProperties().First(p => p.GetCustomAttributes(typeof(PrimaryKey), false).Count() > 0).GetValue(propertyValue, null), null);
                        }
                    }
                    else
                    {
                        property.SetValue(control, (propertyValue as Enum).GetValue(), null);
                    }
                }
            }
        }
    }

    internal class HierarchicalDataProperty
    {
        public Type Type { get; set; }
        public String PropertyName { get; set; }
        public PropertyInfo Property { get; set; }
        public HierarchicalDataProperty ChildProperty { get; set; }
    }
}
